package org.maiyoframework.core.web.fileupload;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

/**
 * springmvc 文件上传组件
 * 在使用配置文件配置 CommonsMultipartResolver bean 时
 * setMaxInMemorySize 将不会再起作用
 * 
 * @author maiYo
 *
 */
@SuppressWarnings("unused")
public class SpringUploadFactory {

	/**
	 * 默认单个文件大小限制2M
	 */
	private static final int default_fileSizeMax = 1024 * 1024 * 2;
	/**
	 * 默认request请求最大值20M
	 */
	private static final int default_maxUploadSize = 1024 * 1024 * 20;
	/**
	 * 默认内存缓存大小
	 */
	private static final int default_maxInMemorySize = 1024 * 1024;
	/**
	 * 默认允许上传文件的格式
	 */
	private static final String default_allowExt = ".jpg.gif.png.doc.rar.txt";
	
	/**
	 * 根据path上传路径创建一个新 SpringUploadFactory 实例
	 * 
	 * @param path
	 *            上传路径
	 */
	public SpringUploadFactory(String path) {
		super();
		this.path = path;
	}
	
	/**
	 * 根据path上传路径，单个文件大小限制和允许上传文件的格式来创建一个新 SpringUploadFactory 实例
	 * @param path 上传路径
	 * @param fileSizeMax 单个文件大小限制
	 * @param allowExt 允许上传文件的格式
	 */
	public SpringUploadFactory(String path, Long fileSizeMax, String allowExt) {
		super();
		this.path = path;
		this.fileSizeMax = fileSizeMax;
		this.allowExt = allowExt;
	}

	/**
	 * 文件上传
	 * 
	 * @param request
	 *            HttpServletRequest
	 * @param fieldName
	 *            表单域名称
	 * @return 上传是否成功
	 */
	public boolean upload(HttpServletRequest request, String fieldName) {
		// 创建一个通用的多部分解析器
        // 如果Spring的配置文件中添加 CommonsMultipartResolver bean
		// 只需要从88行代码处开始 MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
//		CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(request.getServletContext());
//		if(!commonsMultipartResolver.isMultipart(request)){
//			this.setErrorMessage("entype must be multipart/form-data");
//			return false;
//		}
		if (StringUtils.isBlank(getPath())) {
			this.setErrorMessage("请指定上传目录");
			return false;
		}
		// 获取上传目录
		String saveDir = request.getSession().getServletContext().getRealPath("/") + getPath();
		if (!dirExist(saveDir)) {
			this.setErrorMessage("上传目录不存在");
			return false;
		}
		// 以下五行代码同样在使用Spring的配置文件方式配置 CommonsMultipartResolver bean 后不起作用
//		maxInMemorySize = (getMaxInMemorySize() == -1 ? default_maxInMemorySize : getMaxInMemorySize());
//		commonsMultipartResolver.setMaxInMemorySize(maxInMemorySize);
//		maxUploadSize = (getMaxUploadSize() == -1 ? default_maxUploadSize : getMaxUploadSize());
//		commonsMultipartResolver.setMaxUploadSize(maxUploadSize);
//		MultipartHttpServletRequest multipartRequest = commonsMultipartResolver.resolveMultipart(request);
		MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
		List<MultipartFile> mutilfiles = multipartRequest.getFiles(fieldName);
		// 设置单个文件上传大小限制
		fileSizeMax = (getFileSizeMax() == -1 ? default_fileSizeMax : getFileSizeMax());
		uploadfiles = new ArrayList<UploadFile>();
		try {
			for (MultipartFile f : mutilfiles) {
				if (!f.isEmpty()) {
					String originalName = f.getOriginalFilename();
					if (!isAllowExt(originalName)) {
						uploadfiles.add(new UploadFile(originalName, "不允许的文件类型"));
						continue;
					}
					Long singleSize = f.getSize();
					if (singleSize > fileSizeMax) {
						uploadfiles.add(new UploadFile(originalName, "文件大小超过限制" + fileSizeMax / 1000000 + "M"));
						continue;
					}
					String filename = newFileName();
					File file = new File(createSavePath(saveDir), filename);
					f.transferTo(file);
					// 保存到数据库中的文件地址
					String urlPath = getPath() + dateDir + filename;
					uploadfiles.add(new UploadFile(singleSize, urlPath, originalName, filename, fileExt));
				}
			}
		} catch (IllegalStateException e) {
			this.setErrorMessage("未知错误");
			return false;
		} catch (IOException e) {
			this.setErrorMessage("文件上传失败");
			return false;
		}
		return true;
	}
	
	/**
	 * 获取单个文件上传对象
	 * 
	 * @return 单个文件上传对象
	 */
	public UploadFile getSingleUploadFile() {
		List<UploadFile> ufs = getUploadfiles();
		if (ufs != null && ufs.size() > 0) {
			return ufs.get(0);
		}
		return null;
	}
	
	/**
	 * 是否为允许的上传类型
	 * 
	 * @param name
	 *            文件名称
	 * @return boolean
	 */
	private boolean isAllowExt(String name) {
		int index = name.lastIndexOf(".");
		if (index == -1) return false;
		fileExt = name.substring(index + 1);
		allowExt = (getAllowExt() == null ? default_allowExt : getAllowExt());
		if (allowExt.indexOf(fileExt.toLowerCase()) != -1)
			return true;
		return false;
	}
	
	/**
	 * 检查上传目录
	 * 
	 * @param saveDir
	 *            上传目录
	 * @return boolean
	 */
	private boolean dirExist(String saveDir) {
		File dir = new File(saveDir);
		if (!dir.exists() || !dir.isDirectory()) {
			return false;
		}
		return true;
	}
	
	/**
	 * 创建上传文件夹，每月新建一个以年月为名称的文件夹
	 * 
	 * @param saveDir
	 *            上传目录
	 * @return 最终上传目录
	 */
	private String createSavePath(String saveDir) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
		dateDir = sdf.format(new Date()) + "/";
		if (!saveDir.endsWith("/")) saveDir += "/";
		savePath = saveDir + dateDir;
		File dir = new File(savePath);
		if (!dir.exists()) {
			dir.mkdirs();
		}
		return savePath;
	}
	
	/**
	 * 获取新文件名
	 * 
	 * @return 新文件名
	 */
	private String newFileName() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		return sdf.format(new Date()) + new Random().nextInt(1000) + "." + fileExt;
	}

	/**
	 * 自定义文件存储相对路径
	 */
	private String path;
	/**
	 * 上传绝对路径
	 */
	private String savePath;
	/**
	 * 创建的日期文件夹
	 */
	private String dateDir;
	/**
	 * request允许的最大值
	 */
	private Long maxUploadSize = -1L;
	/**
	 * 单个文件允许的最大值
	 */
	private Long fileSizeMax = -1L;
	/**
	 * 内存缓存大小
	 */
	private Integer maxInMemorySize = -1;
	/**
	 * 允许上传文件的格式
	 */
	private String allowExt;
	/**
	 * 文件后缀
	 */
	private String fileExt;
	/**
	 * 上传文件集合
	 */
	private List<UploadFile> uploadfiles;
	/**
	 * 错误提示信息
	 */
	private String errorMessage;
	
	/**
	 * 获取自定义文件存储相对路径
	 * 
	 * @return
	 */
	public String getPath() {
		return path;
	}

	/**
	 * 设置自定义文件存储相对路径
	 * 
	 * @param path
	 */
	public void setPath(String path) {
		this.path = path;
	}

	/**
	 * 获取request允许的最大值
	 * 
	 * @return
	 */
	public Long getMaxUploadSize() {
		return maxUploadSize;
	}

	/**
	 * 设置request允许的最大值
	 * 
	 * @param sizeMax
	 */
	public void setMaxUploadSize(Long maxUploadSize) {
		this.maxUploadSize = maxUploadSize;
	}

	/**
	 * 获取单个文件允许的最大值
	 * 
	 * @return
	 */
	public Long getFileSizeMax() {
		return fileSizeMax;
	}

	/**
	 * 设置单个文件允许的最大值
	 * 
	 * @param fileSizeMax
	 */
	public void setFileSizeMax(Long fileSizeMax) {
		this.fileSizeMax = fileSizeMax;
	}

	/**
	 * 获取允许上传文件的格式
	 * 
	 * @return
	 */
	public String getAllowExt() {
		return allowExt;
	}

	/**
	 * 设置允许上传文件的格式
	 * 
	 * @param allowExt
	 */
	public void setAllowExt(String allowExt) {
		this.allowExt = allowExt;
	}

	/**
	 * 获取上传绝对路径
	 * 
	 * @return
	 */
	public String getSavePath() {
		return savePath;
	}

	/**
	 * 设置上传绝对路径
	 * 
	 * @param savePath
	 */
	public void setSavePath(String savePath) {
		this.savePath = savePath;
	}

	/**
	 * 获取内存缓存大小
	 * 
	 * @return
	 */
	public Integer getMaxInMemorySize() {
		return maxInMemorySize;
	}

	/**
	 * 设置内存缓存大小
	 * 
	 * @param sizeThreshold
	 */
	public void setMaxInMemorySize(Integer maxInMemorySize) {
		this.maxInMemorySize = maxInMemorySize;
	}

	/**
	 * 获取错误提示信息
	 * 
	 * @return
	 */
	public String getErrorMessage() {
		return errorMessage;
	}

	/**
	 * 设置错误提示信息
	 * 
	 * @param errorMessage
	 */
	public void setErrorMessage(String errorMessage) {
		this.errorMessage = errorMessage;
	}

	/**
	 * 获取上传文件集合
	 * 
	 * @return
	 */
	public List<UploadFile> getUploadfiles() {
		return uploadfiles;
	}

	/**
	 * 设置上传文件集合
	 * 
	 * @param uploadfiles
	 */
	public void setUploadfiles(List<UploadFile> uploadfiles) {
		this.uploadfiles = uploadfiles;
	}

}
